package invitation

import (
	"context"
	"encoding/json"
	"fmt"
	"invitation/internal/application"
	"invitation/internal/model"
	"invitation/internal/pkg/ai/zpqy"
	"strings"
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/rs/zerolog/log"
)

// ResponseChecker 定义AI响应检查函数类型
type ResponseChecker func(resp *zpqy.Response) bool

func defaultResponseChecker(resp *zpqy.Response) bool {
	if resp == nil {
		log.Error().Msg("AI response is nil")
		return false
	}

	if len(resp.Choices) == 0 {
		log.Error().Msg("AI response has no choices")
		return false
	}

	content := resp.Choices[0].Message.Content
	if content == "" {
		log.Error().Msg("AI response content is empty")
		return false
	}
	log.Debug().Str("content", content).Msg("AI response content received")
	return true
}

// joinToString 将切片转换为字符串
func joinToString[T any](items []T, toString func(T) string, separator string) string {
	if len(items) == 0 {
		return ""
	}
	strList := make([]string, 0, len(items))
	for _, item := range items {
		strList = append(strList, toString(item))
	}
	return strings.Join(strList, separator)
}

// callAIWithTimeout 带超时的AI调用
func callAIWithTimeout(
	z *zpqy.Client,
	ctx context.Context,
	userInput, prompt string,
	checker ResponseChecker,
) (*zpqy.Response, error) {
	respChan, err := z.ChatCompletion(ctx, userInput, prompt)
	if err != nil {
		return nil, fmt.Errorf("failed to initiate AI call: %w", err)
	}

	select {
	case resp := <-respChan:
		if !checker(resp) {
			return nil, fmt.Errorf("AI service returned an invalid or empty response")
		}
		return resp, nil
	case <-ctx.Done():
		log.Warn().Err(ctx.Err()).Msg("AI call cancelled or timed out via context")
		return nil, fmt.Errorf("AI call cancelled or timed out: %w", ctx.Err())
	}
}

func CallAIWithRetry(
	z *zpqy.Client,
	ctx context.Context,
	userInput, prompt string,
	checker ResponseChecker,
	maxRetries int,
) (*zpqy.Response, error) {
	const (
		initialDelay = 1 * time.Second
		maxDelay     = 10 * time.Second
	)

	var lastErr error

	for attempt := 0; attempt < maxRetries; attempt++ {
		// 检查上下文是否已取消
		if ctx.Err() != nil {
			return nil, fmt.Errorf("context cancelled before attempt %d: %w", attempt+1, ctx.Err())
		}

		// 为每次尝试创建新的子上下文
		callCtx, cancel := context.WithTimeout(ctx, 120*time.Second)
		defer cancel()

		// 记录尝试信息
		log.Debug().
			Int("attempt", attempt+1).
			Int("maxRetries", maxRetries).
			Str("userInput", userInput).
			Msg("Attempting AI call")

		// 执行AI调用
		resp, err := callAIWithTimeout(z, callCtx, userInput, prompt, checker)
		if err == nil {
			log.Debug().
				Int("attempt", attempt+1).
				Msg("AI call succeeded")
			return resp, nil
		}

		// 记录错误
		lastErr = err
		log.Error().
			Err(err).
			Int("attempt", attempt+1).
			Int("maxRetries", maxRetries).
			Msg("AI call failed")

		// 如果不是最后一次尝试，则等待重试
		if attempt < maxRetries-1 {
			// 计算退避时间（指数退避）
			delay := initialDelay * time.Duration(1<<uint(attempt))
			if delay > maxDelay {
				delay = maxDelay
			}

			log.Debug().
				Dur("delay", delay).
				Int("attempt", attempt+1).
				Msg("Waiting before retry")

			// 等待退避时间或上下文取消
			select {
			case <-time.After(delay):
				// 继续下一次尝试
			case <-ctx.Done():
				return nil, fmt.Errorf("context cancelled during backoff: %w", ctx.Err())
			}
		}
	}

	// 所有尝试都失败
	return nil, fmt.Errorf("after %d retries: %w", maxRetries, lastErr)
}

// parseGameSelection 解析AI返回的游戏选择JSON
func parseGameSelection(content string) ([]uint, error) {
	// 定义匹配AI返回JSON格式的结构体
	type GameResponse struct {
		Games []struct {
			GameID uint `json:"game_id"`
		} `json:"games"`
	}

	// 解析JSON内容
	var resp GameResponse
	if err := json.Unmarshal([]byte(content), &resp); err != nil {
		return nil, fmt.Errorf("failed to parse game selection JSON: %w", err)
	}

	// 提取game_id到结果切片
	result := make([]uint, 0, len(resp.Games))
	for _, game := range resp.Games {
		result = append(result, game.GameID)
	}

	return result, nil
}

// parseTeacherSelection 解析AI返回的导师选择JSON
func parseTeacherSelection(content string) ([]string, error) {
	type TeacherResponse struct {
		Teachers []struct {
			TeacherID string `json:"teacher_id"`
		} `josn:"teachers"`
	}
	var resp TeacherResponse
	if err := json.Unmarshal([]byte(content), &resp); err != nil {
		return nil, fmt.Errorf("failed to parse teacher selection JSON: %w", err)
	}
	result := make([]string, 0, len(resp.Teachers))
	for _, teacher := range resp.Teachers {
		result = append(result, teacher.TeacherID)
	}

	return result, nil

}

// fetchDataWithHandler 封装数据获取和错误处理
func fetchDataWithHandler[T any](fetchFunc func() ([]T, error), errMsgPrefix string) ([]T, error) {
	data, err := fetchFunc()
	if err != nil {
		log.Error().Err(err).Msg(errMsgPrefix)
		return nil, fmt.Errorf("%s: %w", errMsgPrefix, err)
	}
	return data, nil
}

// handleErrorResponse 统一错误处理
func handleErrorResponse(ctx *fiber.Ctx, message string, err error) error {
	log.Error().Err(err).Msg(message)
	return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
		"ok":      false,
		"message": message,
	})
}

// getDetailedGameInfo 获取游戏详细信息
func getDetailedGameInfo(app *application.Application, gameIds []uint) ([]zpqy.GameInfo, error) {
	// 输入验证
	if len(gameIds) == 0 {
		return nil, fmt.Errorf("游戏ID列表不能为空")
	}

	// 批量查询游戏信息
	gameInfos, err := app.RepoManager().GameInfoRepo().FindListByIDs(gameIds)
	if err != nil {
		log.Error().Err(err).Msg("批量查询游戏信息失败")
		return nil, fmt.Errorf("查询游戏信息失败: %v", err)
	}

	// 将游戏信息转换为map便于快速查找
	gameInfoMap := make(map[uint]model.GameInfo, len(gameInfos))
	for _, info := range gameInfos {
		gameInfoMap[info.ID] = info
	}

	// 检查是否所有游戏都找到
	var missingGames []uint
	for _, gameID := range gameIds {
		if _, exists := gameInfoMap[gameID]; !exists {
			missingGames = append(missingGames, gameID)
		}
	}

	if len(missingGames) > 0 {
		log.Error().Uints("missingGameIDs", missingGames).Msg("部分游戏信息未找到")
		return nil, fmt.Errorf("找不到以下游戏信息: %v", missingGames)
	}

	// 准备并发查询
	var wg sync.WaitGroup
	errChan := make(chan error, 2) // 两个并发查询
	gameIntroMap := make(map[uint]model.GameIntroduction)
	gameTheoryMap := make(map[uint]model.GameTheory)

	// 并发查询游戏介绍
	wg.Add(1)
	go func() {
		defer wg.Done()
		intros, err := app.RepoManager().GameIntroductionRepo().FindListByGameInfoIDs(gameIds)
		if err != nil {
			errChan <- fmt.Errorf("查询游戏介绍失败: %v", err)
			return
		}
		for _, intro := range intros {
			gameIntroMap[intro.GameInfoID] = intro
		}
	}()

	// 并发查询游戏理论
	wg.Add(1)
	go func() {
		defer wg.Done()
		theories, err := app.RepoManager().GameTheoryRepo().FindListByGameInfoIDs(gameIds)
		if err != nil {
			errChan <- fmt.Errorf("查询游戏理论失败: %v", err)
			return
		}
		for _, theory := range theories {
			gameTheoryMap[theory.GameInfoID] = theory
		}
	}()

	// 等待所有查询完成
	wg.Wait()
	close(errChan)

	// 检查并发查询中的错误
	var errors []error
	for err := range errChan {
		errors = append(errors, err)
	}
	if len(errors) > 0 {
		log.Error().Errs("errors", errors).Msg("并发查询失败")
		return nil, fmt.Errorf("并发查询失败: %v", errors[0]) // 返回第一个错误
	}

	// 构建结果
	result := make([]zpqy.GameInfo, 0, len(gameIds))
	for _, gameID := range gameIds {
		gameInfo, exists := gameInfoMap[gameID]
		if !exists {
			// 理论上不会走到这里，因为前面已经检查过
			continue
		}

		// 检查关联数据是否存在
		intro, introExists := gameIntroMap[gameID]
		theory, theoryExists := gameTheoryMap[gameID]

		if !introExists {
			log.Error().Uint("gameInfoID", gameID).Msg("游戏介绍未找到")
			return nil, fmt.Errorf("找不到游戏 '%d' 的介绍", gameID)
		}

		if !theoryExists {
			log.Error().Uint("gameInfoID", gameID).Msg("游戏理论未找到")
			return nil, fmt.Errorf("找不到游戏 '%d' 的理论说明", gameID)
		}

		result = append(result, zpqy.GameInfo{
			Name:              gameInfo.Name,
			Introduction:      intro.Introduction,
			TheoryExplanation: theory.TheoryExplanation,
		})
	}

	return result, nil
}

// getDetailedTeacherInfo 获取导师详细信息
func getDetailedTeacherInfo(app *application.Application, teacherIDs []string) ([]zpqy.TeacherInfo, error) {
	selectedTeachers := make([]zpqy.TeacherInfo, 0, len(teacherIDs))

	for _, teacherID := range teacherIDs {
		teacherInfo, err := app.RepoManager().FirstTimeTeacherInfoRepo().FindOneByTeacherID(teacherID)
		if err != nil {
			log.Error().Err(err).Msgf("Failed to find first time teacher info for teacher ID: %s", teacherID)
			return nil, fmt.Errorf("找不到ID为 '%s' 的导师信息", teacherID)
		}

		selectedTeachers = append(selectedTeachers, zpqy.TeacherInfo{
			Name: teacherInfo.Name,
			Desc: teacherInfo.Desc,
		})
	}

	return selectedTeachers, nil
}
